Skip to content

feat: covariant self-returns, use-const/enum-name parse edges, exception $previous, unknown-array foreach keys#473

Merged
nahime0 merged 8 commits into
illegalstudio:mainfrom
chadmandoo:upstream-pr/conformance-and-parse-completeness
Jul 20, 2026
Merged

feat: covariant self-returns, use-const/enum-name parse edges, exception $previous, unknown-array foreach keys#473
nahime0 merged 8 commits into
illegalstudio:mainfrom
chadmandoo:upstream-pr/conformance-and-parse-completeness

Conversation

@chadmandoo

Copy link
Copy Markdown
Contributor

Five bounded completeness fixes found compiling real strict-typed PHP:

  • INTERFACE COVARIANCE: an implementation may return a narrower type than the interface declares (the PSR-7 withX(): static shape). type_accepts can never see it — the class under validation is not yet registered mid-construction — so conformance accepts a self-typed return from its own context: the validated interface (or one it extends), or any interface the class declares (or one those extend).
  • PARSE: use const PHP_INT_MAX; — the lexer eagerly tokenizes such constants; the use-declaration prefix accepts the dedicated tokens by canonical spelling (imports are inert — expression uses lower to literals). class Enum / new Enum / Vendor\Enumenum is a soft keyword (vendor precedent: marc-mabe/php-enum); parse_name and the class-decl name accept it.
  • EXCEPTIONS: builtin exception constructors accept PHP's third $previous parameter (positional or named). Not stored — the compact throwable payload has no previous slot and getPrevious() is already synthesized as null — wrap-and-rethrow code compiles with byte-identical message/code round-trips.
  • FOREACH KEYS: iterating an unknown-element array (array-hinted param/property) types keys as Mixed, not Int — the value may be associative at runtime.

Byte-parity vs PHP 8.5 on all five regression tests.

chadmandoo added a commit to chadmandoo/elephc that referenced this pull request Jul 10, 2026
….1 (R6/illegalstudio#595)

Three v0.26.0 enum/parser fixes, survey-driven (79% → 85%, 241 → 262/307):

- ENUM-CASE PARAM DEFAULT (illegalstudio#468, declarations.rs): `E $x = E::Case` — an
  enum-case default has the enum object type, but syntactic inference types
  every `::` access as Str. When the declared type is Object(E) and the
  default is a scoped access naming E, accept it (the semantic pass validates
  the case exists). Clears the Badge/Button `$variant expects Object, got Str`
  family (9).
- KEYWORD ENUM-CASE NAMES (body.rs): PHP 8 allows semi-reserved keywords as
  enum-case names (`case Default;`, `case String;`, `case Float;`) — every one
  but `class` (reserved for `Foo::class`). Clears CardVariant/LogicalType/
  HeadAssetMode `Expected case name after 'case'` (10).
- USE CONST/FUNCTION IMPORTS (illegalstudio#473/EC-10/illegalstudio#493, namespace_use.rs): the lexer
  eagerly tokenizes `PHP_INT_MAX` etc., so the use-declaration parser must
  accept those dedicated tokens as import names (`use const PHP_INT_MAX;`).
  Clears RegionEntry `Expected imported name after 'use'` (2).

Deferred (not survey-visible on v0.26.1): illegalstudio#469 keyword-case access, illegalstudio#472 clone.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…es, exception $previous, unknown-array foreach keys

Four bounded completeness fixes:

- Interface covariant self-returns: an implementation may return a NARROWER
  type than the interface declares (`withX(): static` against an
  interface-typed return, the PSR-7 wither shape). The class under
  validation is mid-construction when conformance runs, so the covariance is
  proven from the conformance context itself.
- Parse edges: `use const PHP_INT_MAX;` (the lexer eagerly tokenizes such
  constants, so the use-declaration parser must accept the dedicated tokens
  as import names) and a class/enum declared with a keyword-spelled name.
- Exception `$previous`: the builtin Exception/Error constructor accepts its
  third `?Throwable $previous` argument.
- foreach KEYS over an unknown-element array (an `array`-hinted param whose
  elements are known only to phpdoc) type as Mixed, not Int — the value may
  be associative at runtime (a header-map shape with string keys).

Byte-parity vs PHP 8.5 on all regression tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chadmandoo
chadmandoo force-pushed the upstream-pr/conformance-and-parse-completeness branch from cce7e0f to ea5b326 Compare July 11, 2026 14:13
@github-actions github-actions Bot added area:codegen Touches target-aware assembly or backend lowering. area:parser Touches parsing or AST construction. area:types Touches type checking, inference, or compatibility. size:m Medium-sized pull request. type:feature Introduces new user-visible behavior or capabilities. labels Jul 13, 2026
nahime0 added 5 commits July 19, 2026 17:36
Store Exception/Error $previous so getPrevious() round-trips, route
?Throwable methods through compact intrinsics, finish covariant self
returns at wither/override sites, and accept enum as a bareword type
name plus use-const aliases for lexer-tokenized globals.

nahime0 commented Jul 19, 2026

Copy link
Copy Markdown
Member

Follow-up review fixes have been prepared locally on top of the currently published PR head (ea5b32659). These commits are not pushed yet; they will appear in the PR diff after the next branch push.

What changed relative to the published version:

  • Merged the current origin/main into the branch and verified a clean merge tree.
  • Removed the unsound with* return-type heuristic. Interface calls now keep their declared return type instead of assuming the receiver interface, preventing invalid descendant-only method chains from compiling.
  • Completed real covariant self-return validation for both instance and static interface methods, and added coverage for static parent overrides.
  • Completed enum soft-keyword handling across enum/class-like declarations, type hints, import aliases, qualified names, scoped expressions, and statement-position disambiguation such as Enum::X;.
  • Unified use const name parsing so lexer-tokenized constants work in single imports, comma-separated imports, grouped imports, and aliases.
  • Finished the Throwable::$previous integration across compact payload allocation and all target-specific throw paths, including retain/null handling, getPrevious(), JSON/SPL/error allocation sites, and the ?Throwable type contract.
  • Kept unknown-array foreach keys as mixed integer/string keys instead of forcing them to integers.
  • Fixed the related guardrails and handoff material: assembly-comment alignment, the stale 32-byte JsonException payload comment, changelog wording, Rust docblocks, and the exceptions example showing a wrapped exception chain.

Validation completed locally:

  • cargo build and cargo fmt --check
  • 15 focused end-to-end codegen regressions
  • focused parser and diagnostic regressions
  • examples/exceptions compiled and ran with wrapped failure <- root cause
  • focused Linux x86_64 Docker regression for Exception::$previous
  • git diff --check, diff-aware assembly-comment validation, and a clean merge tree against origin/main

The full supported-target matrix is intentionally left to CI after the branch is pushed.

…ce-and-parse-completeness

# Conflicts:
#	CHANGELOG.md
#	src/codegen/lower_inst/objects.rs
@github-actions github-actions Bot added scope:multi-area Touches more compiler areas than the automatic area-label cap. size:l Large pull request. and removed size:m Medium-sized pull request. labels Jul 20, 2026
@nahime0
nahime0 merged commit 2cf2f99 into illegalstudio:main Jul 20, 2026
113 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:codegen Touches target-aware assembly or backend lowering. area:parser Touches parsing or AST construction. area:types Touches type checking, inference, or compatibility. scope:multi-area Touches more compiler areas than the automatic area-label cap. size:l Large pull request. type:feature Introduces new user-visible behavior or capabilities.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants